CloudFromationのAWS::Includeを利用してAWS SAMからインラインSwaggerを分離して管理する
はじめに
こんにちは、中山です。
米国時間3/28にCloudFormationのアップデートが発表されました。複数のアップデートがあったのですが、本エントリではその中からAWS::Includeについて、AWS Serverless Application Model(以下 AWS SAM)と交えながらご紹介したいと思います。
AWS::Includeとは何か
一言で説明すると、 CloudFromationにおけるプログラミング言語のモジュールに相当する機能 です。S3にアップロードしたCloudFormationのテンプレートを、別のテンプレートからIncludeすることができます。今までも、ネストスタックやクロススタック参照を利用することで、テンプレートを機能毎に分離して管理することは可能でした。ただし、それぞれ AWS:Include
とは似て非なるものなので、以下のような機能はありませんでした。
- 複数のテンプレートから1つのスタックを作成する
AWS::Include
をトップレベルで利用することにより可能となった- ネストスタックの場合は複数のスタックを作成してしまうので、スタック最大作成数の上限に達してしまう問題があった
- あるリソースのプロパティの一部を別テンプレートから呼び出す
AWS::Include
をリソースのプロパティに指定することにより可能となった
AWS::Include
を使うことでこういったことが可能になりました!CloudFormationがより「コード化」したと言えますね。
ありがちなユースケースを考えてみます。例えば、社内で決められた規定に沿ったVPC/サブネット用のテンプレートを作成したとします。サブネットをパブリック/プライベート/データストアの3階層に分けるようなものを想定してください。こういったテンプレートを作成しておき、S3にアップロードしておくことで、別のテンプレートからそれを再利用できるという訳です。または、プロパティが長くなりがちなリソースをテンプレートで利用する場合、 AWS::Include
で別テンプレートからIncludeすることにより、面倒な作業から開放してくれます。DRY原則の導入ですね!
執筆時点(3/30)では、S3へのアップロードは事前に実施しておく必要があります。ただし、こちらのイシューでAWSの中の人達がコメントしていますが、AWS CLIの aws cloudformation package
コマンドに対応しそうです。これが対応してくれるとより便利に使えると思います。
AWS SAMとの連携
以前AWS SAMとSwaggerを連携させる方法を以下のエントリにまとめました。
上記エントリでは、Swaggerファイルをテンプレート内にインラインで定義する場合、テンプレートを分離して扱うのが現状難しいため、行数が長くなってしまうと記述しました。が、 AWS::Include
の登場により、インラインSwaggerかつテンプレートの分離が簡単に作成可能になりました。
例えば、インラインSwaggerのファイルを以下のように定義したとします。インラインなのでCloudFormationの組み込み関数が利用可能です。ただし、 AWS::Include
でIncludeする場合、現状組み込み関数の短縮表記はサポートされてない点は注意してください。
swagger: 2.0 info: title: Fn::Sub: swagger-transform-1-${Stage} description: Fn::Sub: swagger-transform-1-${Stage} version: 1.0.0 schemes: - https basePath: Fn::Sub: /${Stage} paths: /: get: summary: Root description: | Root Method. consumes: - application/json produces: - application/json parameters: - name: number in: query description: Some number required: true type: number format: integer responses: "200": description: 200 response schema: $ref: "#/definitions/Empty" x-amazon-apigateway-integration: responses: default: statusCode: 200 uri: Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Func1.Arn}/invocations passthroughBehavior: when_no_templates httpMethod: POST type: aws requestTemplates: application/json: | { #foreach($key in $input.params().querystring.keySet()) "$key": "$util.escapeJavaScript($input.params().querystring.get($key))" #if($foreach.hasNext),#end #end } definitions: Empty: type: object title: Empty Schema
このテンプレートをIncludeするAWS SAMを以下のように記述します。
--- AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: swagger-transform-1 Parameters: Stage: Type: String Default: dev ArtifactBucket: Type: String Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: General Configuration Parameters: - Stage - ArtifactBucket ParameterLabels: Stage: default: Stage ArtifactBucket: default: Artifact Bucket Resources: Api: Type: AWS::Serverless::Api Properties: StageName: !Ref Stage DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: !Sub s3://${ArtifactBucket}/swagger.${Stage}.yml Func1: Type: AWS::Serverless::Function Properties: CodeUri: src/handlers/func1 Handler: index.handler Runtime: python2.7 Events: PostApi: Type: Api Properties: Path: / Method: GET RestApiId: !Ref Api Outputs: ApiUrl: Value: !Sub https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}
- 33 - 36行:
AWS::Include
を利用して、DefinitionBody
プロパティを別テンプレートから呼び出しています- 今回は、Swaggerを検証/開発用途で分ける方式(
swagger.dev.yml
/swagger.prod.yml
)にしてみました
最終的なディレクトリ構成は以下の通りです。
$ tree . . ├── params │ ├── param.dev.json │ └── param.prod.json ├── sam.yml └── src ├── api │ ├── swagger.dev.yml │ └── swagger.prod.yml └── handlers └── func1 └── index.py 5 directories, 6 files
この状態でスタックを作成してみましょう。 stage
及び s3_bucket
変数はご自身の環境に合うよう、適宜読み替えてください。
$ aws cloudformation package \ --template-file sam.yml \ --s3-bucket $s3_bucket \ --output-template-file .sam/packaged.yml Uploading to 0906c54356fd357815c158c45a3ff3e5 306 / 306.0 (100.00%) Successfully packaged artifacts and wrote output template to file .sam/packaged.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /path/to/.sam/packaged.yml --stack-name <YOUR STACK NAME> $ aws cloudformation deploy \ --template-file .sam/packaged.yml \ --stack-name test-$stage \ --capabilities CAPABILITY_IAM \ --parameter-overrides $(cat params/param.$stage.json | jq -r '.Parameters | to_entries | map("\(.key)=\(.value|tostring)") | .[]' | tr '\n' ' ' | awk '{print}') Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - test-dev
スタックの作成後、アウトプットに出力されるAPI GatewayのURLにアクセスするとLambda関数が実行されます(今回はJSONを返すだけ)。やりましたね。
$ curl https://stkgjfohn8.execute-api.ap-northeast-1.amazonaws.com/dev -w '\n' {"body": "{\"message\": \"Hello World!\"}", "headers": {"x-custom-header": "My Header Value"}, "statusCode": 200}
まとめ
いかがだったでしょうか。
AWS::Include
の機能と、AWS SAMとの連携方法をご紹介しました。CloudFormationは当初「コード」ではなく「テンプレート」という印象が強かったですが、さまざまなアップデートによりとても使いやすいサービスへと進化しています。今後のアップデートにも期待したいですね。
本エントリがみなさんの参考になれば幸いに思います。